/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package taskgraph;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;

/**
 * Global configuration settings for the taskgraph framework.
 * 
 * <p>This class defines default values for all settings, all of which
 * may be overridden by corresponding property-value pairs in a file
 * named <code>taskgraph.cfg</code>. This file is optional. If this file is located
 * somewhere other than the current working directory, its location
 * must be specified in an environment variable <code>TASKGRAPH_CONFIG</code>.
 * 
 * <p>The following properties may be defined:
 * <p>{@code buffer.capacity} Number of char or byte elements in I/O buffers.
 * The default is 32k.
 * <p>{@code channel.capacity} Number of elements in channel buffers.
 * The default is 32k.
 * <p>{@code logger.base.name} Name of parent package for grouping log entries.
 * Empty by default.
 * <p>{@code logger.config.file} Pathname of log4j properties file.
 * Default is {@code log4j.properties} located in the working directory. 
 *  
 * @author Armando Blancas
 */
public class Config {
	
	private static final String TASKGRAPH_CONFIG_FILE = "taskgraph.cfg";
	private static final String BUFFER_CAPACITY       = "buffer.capacity";
	private static final String CHANNEL_CAPACITY      = "channel.capacity";
	private static final String LOGGER_BASE_NAME      = "logger.base.name";
	private static final String LOGGER_CONFIG_FILE    = "logger.config.file";
	private static final String DEFAULT_LOGGER_NAME   = "taskgraph";
	private static final String DEFAULT_LOGGER_CFG    = "log4j.properties";
	
	private static final int DEFAULT_BUFFER_CAPACITY  = 1024 * 32;
	private static final int DEFAULT_CHANNEL_CAPACITY = 1024 * 32;
	
	
	private int bufferCapacity  = DEFAULT_BUFFER_CAPACITY;
	private int channelCapacity = DEFAULT_CHANNEL_CAPACITY;
	private String loggerName   = DEFAULT_LOGGER_NAME;
	private String loggerConfig = DEFAULT_LOGGER_CFG;
	
	private static Config singleton = new Config();
	
	/**
	 * Gets the sole instance of this class.
	 * 
	 * @return The single {@code Configuration} instance.
	 */
	public static Config get() {
		return singleton;	
	}
	
	/**
	 * Private constructor prevents direct instantiation.
	 */
	private Config() {
		File config = new File(TASKGRAPH_CONFIG_FILE);
		if (config.exists() && config.isFile()) {
			loadProperties(config);
		}
	}

	/**
	 * Gets the number of data elements that I/O buffers will hold.
	 *
	 * <p>The actual allocation in bytes will depend on the type of
	 * each particular channel. Low-level IO is done in bytes or
	 * characters.
	 * 
	 * @return The I/O buffer capacity in units of data.
	 */
	public int bufferCapacity() {
		return bufferCapacity;
	}
	
	/**
	 * Gets the number of data elements channels can hold.
	 * 
	 * <p>The actual allocation in bytes will depend on the type of
	 * each particular channel.
	 * 
	 * @return The channel capacity in units.
	 */
	public int channelCapacity() {
		return channelCapacity;
	}
	
	/**
	 * Looks for the buffer capacity property.
	 * 
	 * @param p The properties collection.
	 */
	private void loadBufferCapacity(Properties p) {
		String s = p.getProperty(BUFFER_CAPACITY, 
				                 String.valueOf(DEFAULT_BUFFER_CAPACITY));
		try {
			int value = Integer.parseInt(s);
			bufferCapacity = Math.max(value, DEFAULT_BUFFER_CAPACITY);
		} catch (NumberFormatException e) {
			// best effort
		}
	}
	
	/**
	 * Looks for the channel capacity property.
	 * 
	 * @param p The properties collection.
	 */
	private void loadChannelCapacity(Properties p) {
		String s = p.getProperty(CHANNEL_CAPACITY, 
				                 String.valueOf(DEFAULT_CHANNEL_CAPACITY));
		try {
			int value = Integer.parseInt(s);
			channelCapacity = Math.max(value, DEFAULT_CHANNEL_CAPACITY);
		} catch (NumberFormatException e) {
			// best effort
		}
	}
	
	/**
	 * Looks for the logger base name.
	 *  
	 * @param p The properties collection.
	 */
	private void loadLoggerBaseName(Properties p) {
		String s = p.getProperty(LOGGER_BASE_NAME, "");
		s = s.trim();
		if (s.length() > 0) {
			loggerName = String.format("%s.%s", s, loggerName);
		}
	}

	/**
	 * Looks for the logger config file.
	 *  
	 * @param p The properties collection.
	 */
	private void loadLoggerConfig(Properties p) {
		String s = p.getProperty(LOGGER_CONFIG_FILE, DEFAULT_LOGGER_CFG);
		s = s.trim();
		if (s.length() > 0)
			loggerConfig = s;
	}

	/**
	 * Loads the configuration file and tried to load any properties in there.
	 * 
	 * @param config A {@code File} object for the config file.
	 */
	private void loadProperties(File config) {
		Properties properties = new Properties();
		BufferedReader reader = null;
		try {
			reader = 
	    		new BufferedReader(
	    			new InputStreamReader(new FileInputStream(config)),
					DEFAULT_BUFFER_CAPACITY);

			properties.load(reader);
			
			loadBufferCapacity(properties);
			loadChannelCapacity(properties);
			loadLoggerBaseName(properties);
			loadLoggerConfig(properties);			
		} catch (IOException e) {
			// best effort
		} finally {
			try {
				if (reader != null)
					reader.close();
			} catch (IOException e) {
				// best effort
			}
		}
	}

	/**
	 * Gets the name of the log4J configuration file.
	 * 
	 * <p>The default is {@code log4j.properties}.
	 * 
	 * @return The log4J config file as a {@code String}.
	 */
	public String loggerConfig() {
		return loggerConfig;
	}
	
	/**
	 * Gets the log4J logger name.
	 * 
	 * <p>The default is {@code taskgraph}.
	 * 
	 * @return The logger name as a {@code String}.
	 */
	public String loggerName() {
		return loggerName;
	}
	
}
